#include "macros.h"
#include "player.h"
#include "game.h"

#include <cassert>
#include <set>
#include <algorithm>
#include <SDL.h>

//地图同行状态查找表
std::array<bool, 104> mapPassthroughLookupTable = {
	false, true, false, true, true, true, true, false, true, false, false, true, true, true, true, false,
	true, true, true, true, true, true, false, false, true, true, true, true, true, true, false, false, false,
	false, false, false, false, false, false, false, true, true, true, true, false, false, false, false, true, true,
	true, true, false, false, false, false, true, true, true, true, false, false, false, false, true, true, true, true,
	false, false, false, false, true, true, true, true, false, false, false, false, false, false, false, false, false, false,
	false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false
};

std::array<char, 22> var_byte_arr_m_2 = {
	2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1
};

std::array<char, 13>var_byte_arr_n_2 = {
	2, 3, 3, 3, 6, 3, 3, 6, 3, 3, 3, 3, 3
};

//难度倍率数组，该数组会影响怪物乘以怪物的基础攻防可以得到实际攻防
static int difficulityArray[] = {
	1, 30, 60, 80, 100
};


static int equipItemTypeArray[] = {
	0, 33, 34, 35, 79, 36, 0, 37, 38, 39, 80, 40
};

std::vector<std::vector<int>> objectAnimSequenceArray = {
		{0},
		{0, 1, 0, 2},
		{0, 1, 2},
		{0, 1, 2, 1},
		{0, 1, 2, 2, 1, 0},
		{0, 1, 2, 3, 2, 1},
		{0, 1, 2, 3, 4},
		{3, 4, 5, 6},
		{4, 3, 2, 1, 0},
		{2, 1, 0},
		{2, 3, 4}
};

std::vector<std::vector<int>> var_byte_arr_arr_b = {
	{6, -1, -79, 0, 3, -26, -5, 0, 4, 11, 6, 0, 5, -25, -53, 0, 0, -16, -27, 0},
	{7, -1, -80, 0, 3, -26, -5, 0, 4, 11, 6, 0, 5, -25, -54, 0, 2, -16, -26, 0},
	{8, -1, -81, 0, 3, -26, -5, 0, 4, 11, 6, 0, 5, -25, -55, 0, 1, -16, -25, 0}
};

std::vector<std::vector<int>> var_byte_arr_arr_a = {
	{0, 0, 0, 33, 56}, {0, 33, 0, 33, 56}, {0, 66, 0, 33, 56},
	{2, 0, 0, 18, 26}, {3, 0, 0, 18, 34}, {1, 0, 0, 55, 61},
	{4, 0, 0, 14, 26}, {4, 14, 0, 14, 26}, {4, 28, 0, 24, 26}
};

//错误提示信息
static std::string gameErrorString[] = {
	u8"无法再下一层了，这是你达到的最底层",
	u8"无法再上一层了，这是你达到的最高层",
	u8"无法再下去了。",
	u8"无法再上去了。",
	u8"俺，当世神界第一斗者，孙!悟!空! 自从受封为齐天大圣，掌管蟠桃园以来，一直逍遥快活，无拘束……",
	u8"你拥有更强力的装备，因此将捡到的丢弃了。",
	u8"直到那一天，遇到了她，在筋斗云上的我，竟然第一次心潮起伏，有了晕机的感觉……",
	u8"神仙动了感情，往往会万劫不复，\n这一次，让我付出了五百年的时间去忘记她……\n五指山脚下的沙子，掠过我的脸庞。\n沙子，跟时间一样，同样随风流逝；同样掩埋过去；\n多少次伸手想抓住，却从指隙溜走……\n看夜空，半梦半醒间，往事历历上心头……"
};

std::string var_java_lang_String_arr_g[]{
	u8"", u8"",
	u8"有些门不能用钥匙打开，只有当你打败它的守卫后才会自动打开。",
	u8"你购买了礼物后再与天宫商人对话，他会告诉你一些重要的消息。",
	u8"",
	u8"我听说在天宫中有2把隐藏的红钥匙。",
	u8"在这个区域不多次提升攻击力，就不能打败“杨戬副手”。切记前人教训！",
	u8"太上老君就在25楼。以你现在的状态去攻击他简直就是自杀。 你应当在取得更高级别的道具后再去打败他。",
	u8"不找到所有的暗墙29楼的暗道是不会打开的",
	u8"如果你到27楼时状态为：生命1500、攻击80、防御98、拥有1把蓝钥匙、5把黄钥匙。那么祝贺你，你的前期是比较成功的。",
	u8"六丁六甲的攻击力太高了，你最好到能对他一击必杀时再与他战斗。",
	u8"别匆忙，放慢速度。",
	u8"如果你能用好4种移动宝物，你不用与强敌作战就能上楼。",
	u8"",
	u8"你需要用“玄明石”取出37楼仓库内的所有宝物。",
	u8"谜题：“在3点，拥有传送功能的密宝就会出现。”",
	u8"“巫师”会用魔法攻击路过的人，在2个“太上老君护卫”间通过会使你的生命减少一半。",
	u8"44楼，被藏在异空间，你只能用密宝才能到达。",
	u8"41楼事实上是左右对称的。",
	u8"像骰子上5的形状是一种封印魔法，你最好记住它在你与49楼假魔王战斗时有用",
	u8"",
	u8"你好，我是太白金星。你最好别见敌人就杀，先往上走，拿到武器和防具再做打算。"
};

//NPC帮助信息
std::string helpNpcTalks[]{
	u8"感谢你救了我，这是1000金就送给你吧。",
	u8"试下火眼金睛吧，你能看到怪物的信息和战斗损失的血量，你可以在物品栏中使用它。",
	u8"",
	u8"",
	u8"很好，你居然找到了我，作为奖励我将给你一瓶千年月桂露，喝了它将按你的攻击力和防御力的综合增加的你生命点数，你越晚使用它效果越好。",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"感谢你救了我，这是1000金就送给你吧。",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"",
	u8"哈喽，送你1000金作为见面礼，记得经常来找我哦。",
	u8""
};

// 商人购买信息
std::string businessManTalks[]{
	u8"大圣饶命，我可以提升3%攻击力和防御力，需要小的为你效劳吗？",
	u8"",
	u8"大圣爷，我这里有1把蓝钥匙，你给50金币吧。",
	u8"我有5把黄钥匙，一共50金币",
	u8"嘿嘿，我有很多把黄钥匙，1把1000金币",
	u8"我有1把红钥匙，只要800金币",
	u8"我有1把蓝钥匙，只要200金币",
	u8"我跟其他人不一样，你可以把多余的钥匙卖给我。100金币回收1把黄钥匙",
	u8"我有1把黄钥匙，1把蓝钥匙，一共是1000金币",
	u8"我有3把黄钥匙，只要200金币", "我有3把蓝钥匙，收你2000金币",
	u8"我可以恢复你2000点血，不过要1000金币",
	u8"我有个宝物，要收你4000金币，是个玄明石。"
};

std::string var_java_lang_String_arr_j[]{
	u8"",
	u8"",
	u8"天宫一共50层，每10层为一个区域。如果不打败该区域的头目就不能到更高的地方。",
	u8"在商店里你最好选择提升防御力，只有在攻击力低于敌人的防御力时才提升攻击力。",
	u8"",
	u8"你是否注意到5、9、14、16、18楼有的墙与众不同？",
	u8"如果你持有太公杖，面对天神力士和巨灵神时你的攻击力加倍。在没有太公杖的情况下你是无法打败巨灵神的。太公杖被藏在15楼以上的墙内。",
	u8"",
	u8"天宫一共有50层，但50楼并不能直接上去。",
	u8"存放乌金棍的房间的门坏了，你必须用金勺子破墙而入。",
	u8"天宫中藏有有个“幸运金币”拥有它在打败敌人后能够获得2倍的金钱。",
	u8"“紫金龙鳞甲”能防御“太上老君护卫”的夹击，但它被深藏在神秘的楼层中。",
	u8"如果要打败杨戬你需要“乌金棍”、“银缕锁甲”、“捆仙绳”或更高等级的宝物。"
};

Game::Game(const std::filesystem::path& srcRoot)
	:m_srcRoot(srcRoot / "resources"), m_dataRoot(srcRoot / "data")
{
	srand(time(NULL));
	m_gameData.m_player = std::make_shared<Player>();
}

Game::~Game()
{
}

bool Game::init()
{
	// Setup SDL
	// (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems,
	// depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!)
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
		const char* err = SDL_GetError();
		printf("Error: %s\n", err);
		return false;
	}

	//加载level
	m_gameData.maps.resize(TOTAL_LEVEL_COUNT);
	return true;
}



void Game::cleanup()
{
	SDL_GL_DeleteContext(gl_context);
	SDL_DestroyWindow(m_window);
	SDL_Quit();
}

bool Game::createWindow(int width, int height)
{
	m_window = SDL_CreateWindow((std::string(u8"囧囧西游 Remake 第") + std::to_string(m_gameData.level) + u8"层").c_str(),
		SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
	SDL_UpdateWindowSurface(m_window);
	// 创建渲染器
	auto renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
	// Update the window display
	SDL_UpdateWindowSurface(m_window);
	return true;
}

bool Game::initResources()
{
	SDL_Renderer* pRenderer = SDL_GetRenderer(m_window);
	return ResourceMgr::get().init(pRenderer, m_srcRoot, m_dataRoot);
}

void Game::exec()
{
	// Our state
	//ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
	//加载level
	changeLevel(1);
	//启动动画定时器
	startFloatingTimer(120);
	startAnimTimer(200);
	// Main loop
	bool done = false;
	while (!done) {
		SDL_Event event;
		while (SDL_PollEvent(&event)) {
			if (event.type == SDL_QUIT)
				done = true;
			if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(m_window))
				done = true;
			if (event.type == SDL_KEYDOWN) {
				//按键按下，更新状态
				switch (event.key.keysym.sym) {
				case SDLK_w: {
					m_gameData.m_player->direction = MoveDirection::Up;
					m_gameData.m_player->isMoveing = true;
				}break;
				case SDLK_s: {
					m_gameData.m_player->direction = MoveDirection::Down;
					m_gameData.m_player->isMoveing = true;
				}break;
				case SDLK_a: {
					m_gameData.m_player->direction = MoveDirection::Left;
					m_gameData.m_player->isMoveing = true;
				}break;
				case SDLK_d: {
					m_gameData.m_player->direction = MoveDirection::Right;
					m_gameData.m_player->isMoveing = true;
				}break;
				}
			}
			if (event.type == SDL_KEYUP) {
				switch (event.key.keysym.sym) {
#ifdef _DEBUG
				case SDLK_UP: {
					if (m_gameData.level + 1 < 55) {
						changeLevel(m_gameData.level + 1);
						SDL_SetWindowTitle(m_window, (std::string(u8"囧囧西游 Remake 第") + std::to_string(m_gameData.level) + u8"层").c_str());
					}
				}break;
				case SDLK_DOWN: {
					if (m_gameData.level - 1 >= 0) {
						changeLevel(m_gameData.level - 1);
						SDL_SetWindowTitle(m_window, (std::string(u8"囧囧西游 Remake 第") + std::to_string(m_gameData.level) + u8"层").c_str());
					}
				}break;
#endif
				case SDLK_w: {
					if (m_gameData.m_player->direction == MoveDirection::Up)
						m_gameData.m_player->isMoveing = false;
				}break;
				case SDLK_s: {
					if (m_gameData.m_player->direction == MoveDirection::Down)
						m_gameData.m_player->isMoveing = false;
				}break;
				case SDLK_a: {
					if (m_gameData.m_player->direction == MoveDirection::Left)
						m_gameData.m_player->isMoveing = false;
				}break;
				case SDLK_d: {
					if (m_gameData.m_player->direction == MoveDirection::Right)
						m_gameData.m_player->isMoveing = false;
				}break;
				default:
					break;
				}
			}
		}

		//handle game logic
		auto timeNow = SDL_GetTicks();
		if (timeNow - m_logicTimestamp > 100) {
			processGamelogic(timeNow - m_logicTimestamp);
			m_logicTimestamp = timeNow;
		}

		auto renderer = SDL_GetRenderer(m_window);
		//render to texture
		SDL_SetRenderTarget(renderer, m_gameData.gameTexture.get());
		SDL_RenderClear(renderer);
		paintGame();
		//copy texture back to window
		SDL_SetRenderTarget(renderer, nullptr);
		int width = 0, height = 0;
		SDL_QueryTexture(m_gameData.gameTexture.get(), nullptr, nullptr, &width, &height);
		SDL_Rect srcRect{ 0,0, width, height };
		SDL_Rect dstRect{ 50,50, static_cast<int>(width * 1.2), static_cast<int>(height * 1.2) };
		//clear the window target before we copy game texture.
		SDL_RenderClear(renderer);
		SDL_RenderCopy(renderer, m_gameData.gameTexture.get(), &srcRect, &dstRect);
		SDL_RenderPresent(renderer);
	}
	//stop anim timer
	stopAnimTimer();
}

void Game::paintGame()
{
	//游戏绘制逻辑
	SDL_Renderer* renderer = SDL_GetRenderer(m_window);
	auto tileTexture = ResourceMgr::get()["map"][0];
	auto pMap = m_gameData.maps[m_gameData.level];
	for (int row = 0; row < pMap->mapHeight * 2; row++) {
		for (int col = 0; col < pMap->mapWidth * 2; col++) {
			int passableValue = pMap->mapTileIndexArray[row * pMap->mapWidth * 2 + col];
			if (passableValue > 0) {
				int controlValue = pMap->mapTileTransformArray[row * pMap->mapWidth * 2 + col];
				int x = (passableValue & 7) << 4;
				int y = passableValue >> 3 << 4;
				SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "texture pos: %d, %d", x, y);
				//绘制，注意处理图形的翻转
				SDL_Rect srcRect{ x, y, 16, 16 };
				SDL_Rect dstRect{ col * 16,row * 16, 16, 16 };
				if (controlValue == 0) {
					//更新纹理
					SDL_RenderCopy(renderer, tileTexture->texture, &srcRect, &dstRect);
				} else if (controlValue == 1) {
					//更新纹理 旋转
					SDL_RenderCopyEx(renderer, tileTexture->texture, &srcRect, &dstRect, 0, nullptr, SDL_FLIP_HORIZONTAL);
				}
			}
		}
	}
	//绘制objects
	for (auto& obj : pMap->m_objects) {
		if (!obj.alive) continue;
		paintObject(obj);
	}
	//绘制player
	paintPlayer();
}

void Game::startAnimTimer(int interval)
{
	m_animTimer = SDL_AddTimer(interval, [](Uint32 interval, void* param)->Uint32 {
		auto pData = static_cast<GameState*>(param);
		//遍历当前的对象列表，对仍然存活的对象进行动画更新
		auto pMap = pData->maps[pData->level];
		if (pMap) {
			for (GroundObject& obj : pMap->m_objects) {
				if (!obj.alive || !obj.bVisible)
					continue;
				obj.nextAnim();
			}
		}
		//更新角色动画

		return interval;
	}, &m_gameData);
}

void Game::stopAnimTimer()
{
	if (m_animTimer) {
		SDL_RemoveTimer(m_animTimer);
		m_animTimer = 0;
	}
}

void Game::startFloatingTimer(int interval)
{
	m_floatingTimer = SDL_AddTimer(interval, [](Uint32 interval, void* param)->Uint32 {
		auto pData = static_cast<GameState*>(param);
		//pThis->y = 0;
		//pThis->x = (pThis->x + 1) % 3;

		//修改物品浮动值
		if (pData->bFloatingUp) {
			if (++pData->equipOffsetY > 1) {
				pData->bFloatingUp = false;
			}
		} else if (--pData->equipOffsetY < -1) {
			pData->bFloatingUp = true;
		}

		return interval;
	}, &m_gameData);
}

void Game::stopFloatingTimer()
{
	if (m_floatingTimer) {
		SDL_RemoveTimer(m_floatingTimer);
		m_floatingTimer = 0;
	}
}

void Game::processGamelogic(int interval)
{
	auto& actorInfo = ResourceMgr::get().getActor(0);
	auto pMap = m_gameData.maps[m_gameData.level];
	auto& player = m_gameData.m_player;
	int nextX = player->x;
	int nextY = player->y;

	if (m_gameData.m_player->isMoveing) {
		switch (m_gameData.m_player->direction) {
		case MoveDirection::Up: {
			nextY -= 1;
		}break;
		case MoveDirection::Down: {
			nextY += 1;
		}break;
		case MoveDirection::Left: {
			nextX -= 1;
		}break;
		case MoveDirection::Right: {
			nextX += 1;
		}break;
		default:
			break;
		}

		//首先获取此位置的所有对象


		//如果可以通行，则更新角色坐标
		if (activeObject(nextX, nextY)) {
			player->x = nextX;
			player->y = nextY;
		}
	}
}

bool Game::loadLevel(int level)
{
	if (!m_gameData.maps[level]) {
		auto pMap = std::make_shared<MapData>();
		//加载
		auto levelFile = m_srcRoot / ("maplv" + std::to_string(level));
		if (!std::filesystem::exists(levelFile)) {
			return false;
		}
		//打开文件并读取size
		std::ifstream ifs;
		ifs.open(levelFile, std::ios::binary);
		if (ifs.fail()) {
			return false;
		}
		pMap->mapWidth = readBinaryShort(ifs) / 2;
		pMap->mapHeight = readBinaryShort(ifs) / 2;

		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "load level: %d, width: %d, height: %d", level, pMap->mapWidth, pMap->mapHeight);

		int nSize = pMap->mapWidth * pMap->mapHeight * 4;
		int pixelWidth = pMap->mapWidth * 32;
		int pixelHeight = pMap->mapHeight * 32;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "pixelWidth: %d, pixelHeight: %d", pixelWidth, pixelHeight);
		//猜测是通行路径
		pMap->m_mapCollisionArray.resize(pMap->mapHeight);
		for (auto& line : pMap->m_mapCollisionArray) {
			line.resize(pMap->mapWidth);
		}
		//加载tile信息
		pMap->mapTileIndexArray.resize(nSize);
		pMap->mapTileTransformArray.resize(nSize);
		ifs.read(pMap->mapTileIndexArray.data(), nSize);
		ifs.read(pMap->mapTileTransformArray.data(), nSize);
		//初始化地图通行信息
		initPassthrough(pMap->mapWidth, pMap->mapHeight, pMap->mapTileIndexArray, pMap->m_mapCollisionArray);
		//加载sprite,贴在地形之上
		loadLevelSprites(pMap, m_srcRoot, level);
		//保存到map中
		m_gameData.maps[level] = pMap;
	}
	return true;
}

void Game::initPassthrough(int width, int height, const std::vector<char>& passableArray, std::vector<std::vector<bool>>& result)
{
	int n = 0;
	char objectPassIndex = 0;
	int line = 0;
	while (line < height) {
		int col = 0;
		while (col < width) {
			objectPassIndex = passableArray[n];
			result[line][col] = objectPassIndex < (int)mapPassthroughLookupTable.size() ? mapPassthroughLookupTable[objectPassIndex] : false;
			++col;
			n += 2;
		}
		++line;
		n += width << 1;
	}
}

bool Game::loadLevelSprites(const std::shared_ptr<MapData>& pMap, const std::filesystem::path& basePath, int nLevel)
{
	auto levelFile = basePath / ("sprite" + std::to_string(nLevel));
	if (!std::filesystem::exists(levelFile)) {
		return false;
	}
	std::ifstream ifs;
	ifs.open(levelFile, std::ios::binary);
	if (ifs.fail()) {
		return false;
	}

	short npcFlag = 0;
	int nCount = readBinaryShort(ifs) & 0xFFFF | readBinaryShort(ifs) << 16;
	SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "map object count: %d", nCount);
	//清理
	pMap->m_objects.clear();
	for (int i = 0; i < nCount; i++) {
		int objectId = ifs.get();
		int savedX = readBinaryShort(ifs);
		int savedY = readBinaryShort(ifs);

		savedX = (savedX + 16 >> 5) << 5;
		savedY = (savedY + 16 >> 5) << 5;

		int x = savedX / 32;
		int y = savedY / 32;

		switch (objectId) {
		case 6:		//隐形路径
		case 12: {	//隐形墙
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, 0 ,false };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 9: {	//炼丹炉
			npcFlag = ifs.get();
			GroundObject obj{ objectId,true, GroundObjectState_Normal,x, y, npcFlag };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 5:		//门卫
		case 81: {	//封印门
			npcFlag = ifs.get() + 1;
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, npcFlag ,false };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 83: {	//剧情点
			npcFlag = ifs.get();
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, npcFlag ,false };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 7:		//上楼梯
		case 8: {	//下楼梯
			npcFlag = ifs.get() | ifs.get() << 8;
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, npcFlag };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 4:		//封印门
		case 57:	//赤力韦驮
		case 59:	//伏魔韦驮
		case 70:	//玉皇大帝
		case 71:	//玉皇大帝
		case 72:	//牛魔王
		case 73: {	//天蓬元帅
			npcFlag = ifs.get() + 1;
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, npcFlag };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 77: {	//太白金星
			npcFlag = ifs.get();
			npcFlag = npcFlag & 0xFF | var_byte_arr_m_2[npcFlag - 1] << 8;
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, npcFlag };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 78: {	//天宫商人
			npcFlag = ifs.get();
			npcFlag = npcFlag & 0xFF | var_byte_arr_n_2[npcFlag - 1] << 8;
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, npcFlag };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		case 76:	//路点
		case 82: {	//传怪点
			npcFlag = ifs.get() | (ifs.get() + (1 << 8));
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, npcFlag, false };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}break;
		default: {
			GroundObject obj{ objectId,true,GroundObjectState_Normal, x, y, 0 };
			obj.initPropertys();
			pMap->m_objects.push_back(obj);
		}
		}
	}
	ifs.close();
	return true;
}


bool Game::paintObject(GroundObject& obj)
{
	ActorInfo& actorInfo = ResourceMgr::get().getActor(obj.objectId);
	auto objectSprite = actorInfo.getRenderResource();
	if (!objectSprite)
		return false;

	int frameWidth = obj.frameWidth;
	int frameHeight = obj.frameHeight;
	if (frameHeight == 0) {
		frameHeight = objectSprite->height;
	}

	int pixelX = obj.x * 32;
	int pixelY = obj.y * 32;
	int destOffestX = obj.getAnimIndex() * obj.frameWidth;

	std::shared_ptr<SDL_Texture>& dstTexture = m_gameData.gameTexture;

	//如果已死亡或隐藏，则跳过绘制
	if (!obj.alive || !obj.bVisible)
		return false;

	switch (obj.actorType) {
	case ActorType_GroundItem: {		//地面物体
		if (obj.objectId == 6) {			//隐形路径
			pixelY += 5 - m_gameData.equipOffsetY;
		} else if (obj.objectId == 9) {	//炼丹炉
			pixelX += 32;
		}
		//正在消失，需要更新
		if (obj.state != GroundObjectState_Disappearing) {
			paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 32), destOffestX, 0, frameWidth, frameHeight);
			break;
		}
		//墙壁破碎特效
		auto effectActor = ResourceMgr::get()[4][12];
		paintSprite(effectActor, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 32), destOffestX, 0, frameWidth, frameHeight);
	}break;
	case ActorType_Equip: {			//装备
		//先绘制阴影，再绘制武器
		paintSprite(ResourceMgr::get()[5][0], pixelX + 8, pixelY + 22);
		//绘制浮动动画
		paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 24) + m_gameData.equipOffsetY, destOffestX, 0, frameWidth, frameHeight);
	}break;
	case ActorType_BusinessMan: {	//商人
		if (obj.state == GroundObjectState_Disappearing || obj.state == GroundObjectState_Appearing) {
			//显示云雾的消失和出现特效
			auto shadowSprite = ResourceMgr::get()[2][9];		//商人消失出现时的烟雾特效
			//显示云彩
			paintSprite(shadowSprite, pixelX + 2, pixelY + 2 - (obj.getAnimIndex() << 3), destOffestX, 0, 27, 29);
			break;
		}
		//绘制云彩
		auto cloudSprite = ResourceMgr::get().getActor(15).getRenderResource();
		paintSprite(cloudSprite, pixelX + 1, pixelY + 8 + m_gameData.equipOffsetY, 0, 0, cloudSprite->width, cloudSprite->height);
		//绘制角色
		paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 24) + m_gameData.equipOffsetY, destOffestX, 0, frameWidth, frameHeight);
	}break;
	case ActorType_NPC: {			//NPC
		//阴影效果
		paintSprite(ResourceMgr::get()[5][0], pixelX + 8, pixelY + 22);
		//显示云雾的消失和出现特效
		if (obj.state == GroundObjectState_Disappearing || obj.state == GroundObjectState_Appearing) {
			paintSprite(ResourceMgr::get()[2][9], pixelX + 2, pixelY + 2 - (obj.getAnimIndex() << 3), destOffestX, 0, 27, 29);
			break;
		}
		if (obj.objectId != 72) {		//非牛魔王
			paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 16) + m_gameData.equipOffsetY, destOffestX, 0, frameWidth, frameHeight);
			break;
		}
		//绘制牛魔王
		//当前没有和牛魔王交互，绘制其对象
		paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 32), destOffestX, 0, frameWidth, frameHeight);
		//TODO: 根据朝向绘制牛魔王
		/*
		pixelY = frameHeight * this.var_byte_t;
		if (this.var_byte_t == 3) {
			this.drawImageWithTranslate((Image)object, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 32), destOffestX, pixelY -= frameHeight, frameWidth, frameHeight, 1);
			continue block12;
		}
		this.paintSprite((Image)object, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 32), destOffestX, pixelY, frameWidth, frameHeight);
		*/
	}break;
	case ActorType_Monster: {		//怪物
		//哮天犬和杨戬特殊处理，需要用xtq里的资源配合拼接数组进行拼接
		if (obj.state == GroundObjectState_InBattle) {
			pixelX += rand() % 5 - 2;
			pixelY += rand() % 5 - 2;
		}
		if (obj.state == GroundObjectState_Disappearing || obj.state == GroundObjectState_Appearing) {
			//烟雾
			paintSprite(ResourceMgr::get()[2][9], pixelX + 2, pixelY + 2 - (obj.getAnimIndex() << 3), destOffestX, 0, 27, 29);
		} else if (obj.objectId == 67 || obj.objectId == 69) {		//巨型boss的拼接绘制
			if (obj.objectId == 67) {		//哮天犬 xtq拼接画法
				paintSprite(ResourceMgr::get()[5][0], pixelY + 8, frameWidth + 22);
				int changedX = pixelX + 16;
				int changedY = pixelY + 16;
				int animIndex = obj.getAnimIndex();
				auto& pieces = var_byte_arr_arr_b[animIndex];
				for (int i = 0; i < pieces.size(); i += 4) {
					auto& tileInfo = var_byte_arr_arr_a[pieces[i]];
					int actorType = tileInfo[0];
					paintSprite(ResourceMgr::get()[9][actorType], changedX + pieces[i + 1], changedY + pieces[i + 2], tileInfo[1], tileInfo[2], tileInfo[3], tileInfo[4]);
				}
				break;
			} else if (obj.objectId == 69) {	//杨戬 xtq拼接画法
				//阴影
				paintSprite(ResourceMgr::get()[5][0], pixelX + 40, pixelY + 22);
				//杨戬
				pixelX += 3;
				pixelY -= 49;
				pixelY += m_gameData.equipOffsetY;
				paintSprite(objectSprite, pixelX, pixelY);
				//额外贴图
				if (obj.getAnimIndex() > 0) {
					paintSprite(ResourceMgr::get()[9][5], pixelX + 32, pixelY + 27, 26 * (obj.getAnimIndex() - 1), 0, 26, 13);
				} else {
					break;
				}
			}
		} else {
			paintSprite(ResourceMgr::get()[5][0], pixelX + 8, pixelY + 22);
			paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), (pixelY - 4) - (frameHeight - 32), destOffestX, 0, frameWidth, frameHeight);
		}
	}break;
	case ActorType_PropertyItem: {	//属性物品
		if (obj.state == GroundObjectState_Disappearing) {
			paintSprite(ResourceMgr::get()[4][12], pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 32), destOffestX, 0, frameWidth, frameHeight);
			break;
		}
		if (obj.state == GroundObjectState_Appearing) {
			paintSprite(ResourceMgr::get()[2][9], pixelX + 2, pixelY + 2 - (obj.getAnimIndex() << 3), destOffestX, 0, 27, 29);
			break;
		}
		if (obj.objectId < 26) {	//功能道具
			//绘制阴影
			paintSprite(ResourceMgr::get()[5][0], pixelX + 8, pixelY + 22);
			paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 24) + m_gameData.equipOffsetY);
			break;
		}
		paintSprite(objectSprite, pixelX + (32 - frameWidth >> 1), pixelY - (frameHeight - 30));
	}break;
	default:
		break;
	}
	return true;
}

void Game::changeLevel(int level)
{
	if (m_gameData.level == level) return;
	auto& pNewMap = m_gameData.maps[level];
	if (!pNewMap) {
		bool bOk = loadLevel(level);
		if (!bOk) {
			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "loadLevel: %d failed !", level);
			return;
		}
		pNewMap = m_gameData.maps[level];
	}
	if (!m_gameData.gameTexture) {
		//第一次初始化，创建texture
		auto renderer = SDL_GetRenderer(m_window);
		auto texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, pNewMap->mapWidth * 32, pNewMap->mapHeight * 32);
		m_gameData.gameTexture = std::shared_ptr<SDL_Texture>(texture, SDL_DestroyTexture);
	} else {
		//切换
		auto& pOldMap = m_gameData.maps[m_gameData.level];
		//判断是否需要重新初始化texture
		if (pNewMap->mapWidth != pOldMap->mapWidth || pNewMap->mapHeight != pOldMap->mapHeight) {
			//需要重新创建纹理贴图
			if (m_gameData.gameTexture) {
				//释放旧的
				m_gameData.gameTexture = nullptr;
			}
			//创建新的
			auto renderer = SDL_GetRenderer(m_window);
			auto texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, pNewMap->mapWidth * 32, pNewMap->mapHeight * 32);
			m_gameData.gameTexture = std::shared_ptr<SDL_Texture>(texture, SDL_DestroyTexture);
		}
	}
	m_gameData.level = level;
}

bool Game::checkMapPassable(int x, int y)
{
	auto pMap = m_gameData.maps[m_gameData.level];
	//判断是否超过了地图边界
	if (y >= pMap->m_mapCollisionArray.size() || x >= pMap->m_mapCollisionArray[y].size())
		return false;
	if (!pMap->m_mapCollisionArray[y][x])
		return false;
	return true;
}

std::vector<GroundObject*> Game::getGroundObjectsAtPosition(int x, int y)
{
	std::vector<GroundObject*> objects;
	auto pMap = m_gameData.maps[m_gameData.level];
	for (int i = 0; i < pMap->m_objects.size(); i++) {
		auto* pObj = &pMap->m_objects[i];
		if (pObj->x == x && pObj->y == y) {
			if (pObj->state == GroundObjectState_Disappearing) continue;
			objects.push_back(pObj);
		}
	}
	return objects;
}

bool Game::paintPlayer()
{
	auto& player = m_gameData.m_player;

	ActorInfo& actorInfo = ResourceMgr::get().getActor(0);
	auto renderData = actorInfo.getRenderResource();
	if (!renderData)
		return false;

	int spriteWidth = actorInfo.spriteFrameWidth;
	int spriteHeight = actorInfo.spriteFrameHeight;

	int pixelX = player->x * 32;
	int pixelY = player->y * 32;

	std::shared_ptr<SDL_Texture>& dstTexture = m_gameData.gameTexture;
	int rowIndex = 0;
	bool bFlip = false;
	switch (player->direction) {
	case MoveDirection::Up: {
		rowIndex = 1;
	}break;
	case MoveDirection::Down: {
		rowIndex = 0;
	}break;
	case MoveDirection::Right: {
		rowIndex = 2;
	}break;
	case MoveDirection::Left: {
		rowIndex = 2;
		bFlip = true;
	}break;
	default:
		break;
	}

	SDL_Renderer* pRenderer = SDL_GetRenderer(m_window);
	SDL_Rect srcRect{ spriteWidth * actorInfo.currFrameIdx, rowIndex * spriteHeight, spriteWidth, spriteHeight };
	//拷贝的时候需要下边缘对齐，需要换算坐标
	SDL_Rect dstRect{ pixelX - (spriteWidth - 32) / 2 , pixelY - (spriteHeight - 32), spriteWidth, spriteHeight };
	SDL_RenderCopyEx(pRenderer, renderData->texture, &srcRect, &dstRect, 0, nullptr, bFlip ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE);

	return true;
}

int Game::getBagItemIndex(int objectId)
{
	int index = -1;
	for (int i = 0; i < m_gameData.m_player->m_bagItems.size(); i++) {
		if (m_gameData.m_player->m_bagItems[i].first == objectId) {
			index = i;
			break;
		}
	}
	return index;
}

int Game::caculateHpReduce(int objectId, bool bFlag)
{
	int attack = getRealAttack(objectId);
	auto& actorInfo = ResourceMgr::get().getActor(objectId);
	int multiply = difficulityArray[m_gameData.difficulity];
	return battle(attack, m_gameData.m_player->defence, actorInfo.baseHp * multiply, actorInfo.baseAttack * multiply, actorInfo.baseDef * multiply, bFlag);

}

int Game::getRealAttack(int objectId)
{
	if (objectId == 74 || objectId == 53 || objectId == 49) {	//74 巨灵神 53 金刚力士 49 巨力士
		if (getBagItemIndex(23) >= 0) {						//太公杖
			return m_gameData.m_player->attack * 2;
		}
	} else if (objectId == 69) {								// 69 杨戬
		if (getBagItemIndex(24) >= 0) {						//捆仙绳
			return m_gameData.m_player->attack * 2;
		}
	}
	return m_gameData.m_player->attack;
}

int Game::battle(int playerAttack, int playerDef, int monsterHp, int monsterAttack, int monsterDef, bool bPlayerAttackFirst)
{
	int hpReduce = -1;
	int battleRound = 0;    //战斗回合数
	if (playerAttack > monsterDef) {
		battleRound = monsterHp / (playerAttack - monsterDef);
		//上取整
		if (battleRound * (playerAttack - monsterDef) < monsterHp) {
			++battleRound;
		}
		hpReduce = battleRound > 1 && monsterAttack > playerDef ? (monsterAttack - playerDef) * (battleRound - 1) : 0;
		if (!bPlayerAttackFirst && monsterAttack > playerDef) {
			hpReduce += monsterAttack - playerDef;
		}
	}
	return hpReduce;
}

int Game::getPropertyItemValue()
{
	int n = m_gameData.level - 1;
	if ((n /= 10) < 0) {
		n = 0;
	}
	if (m_gameData.level > 50) {
		n = 0;
	}
	return n + 1;
}

void Game::pickupGroundItem(int objectId)
{
	switch (objectId) {
	case 33:    //桃木棒
	case 34:    //玄铁棒
	case 35:    //真银棒
	case 36:    //金箍棒
	case 37:    //道袍
	case 38:    //铁甲
	case 39:    //锁子甲
	case 40:    //紫金龙鳞甲
	case 79:    //乌金棍
	case 80: {  //银缕锁甲
		if (changeEquipWeaponCloth(objectId, false)) {
			m_gameData.m_player->currentEquipItem = objectId;
			//this.a((byte)1, this.equipItemDescribs[equipIndexInDescribArray], (byte)0, (byte)3);
			auto& equipActor = ResourceMgr::get().getActor(objectId);
			showGameInfoString(equipActor.itemDescrib);
			return;
		}
		//this.a((byte)0, this.var_java_lang_String_arr_a[5], (byte)0, (byte)3);
		showGameInfoString(gameErrorString[5]);
		return;
	}
	case 15: {  //筋斗云
		m_gameData.m_player->isJDYEnabled = true;
	}
	case 13:    //火眼金睛
	case 14:    //生死簿
	case 16:    //芭蕉扇
	case 17:    //金勺子
	case 18:    //玄明石
	case 19:    //千年月桂露
	case 20:    //相形变位
	case 21:    //上行符
	case 22:    //下行符
	case 23:    //太公杖
	case 24:    //捆仙绳
	case 25:    //幸运币
	case 85:    //黄金钥匙
	case 86: {  //日月无极钹
		equipItem(objectId);
	}break;
	case 26: {  //黄钥匙
		++m_gameData.m_player->yellowKey;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "yellow key now: %d", m_gameData.m_player->yellowKey);
	}break;
	case 27: {  //红钥匙
		++m_gameData.m_player->redKey;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "red key now: %d", m_gameData.m_player->yellowKey);
	}break;
	case 28: {  //蓝钥匙
		++m_gameData.m_player->blueKey;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "blue key now: %d", m_gameData.m_player->yellowKey);
	}break;
	case 29: {  //威力丹
		int increase = getPropertyItemValue();
		m_gameData.m_player->attack += increase;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "attack increase %d, now: %d", increase, m_gameData.m_player->attack);

	}break;
	case 30: {  //金刚丹
		int increase = getPropertyItemValue();
		m_gameData.m_player->defence += increase;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "defence increase %d, now: %d", increase, m_gameData.m_player->defence);
	}break;
	case 31: {  //回春丸
		int hp = getPropertyItemValue();
		m_gameData.m_player->hp += 50 * hp;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "hp increase %d, now: %d", hp * 50, m_gameData.m_player->hp);
		//this.updateHp(50 * hp);
	}break;
	case 32: {  //长寿丹
		int hp = getPropertyItemValue();
		m_gameData.m_player->hp += 200 * hp;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "hp increase %d, now: %d", hp * 200, m_gameData.m_player->hp);
		//this.updateHp(200 * hp);
	}break;
	}
}

void Game::equipItem(int objectId)
{
	int itemIndex = getBagItemIndex(objectId);
	auto& actorInfo = ResourceMgr::get().getActor(objectId);
	//如果物品不存在则入包
	if (itemIndex < 0) {
		//将物品入包
		m_gameData.m_player->m_bagItems.push_back({ objectId, actorInfo.useCount });
	} else {
		//已经在背包里了，再次捡到增加使用数量（这类物品一般都只有一个）
		m_gameData.m_player->m_bagItems[itemIndex].second += actorInfo.useCount;
	}
	//TODO: 显示字符串
	//this.playerCurrentEquipItemType = (byte)objectId;
	//this.a((byte)1, this.var_java_lang_String_arr_k[n3], (byte)0, (byte)3);
}

bool Game::changeEquipWeaponCloth(int equipObjectId, bool forceEquip)
{
	bool result = true;

	auto& equipActor = ResourceMgr::get().getActor(equipObjectId);
	if (equipActor.useCount == -2) {		//武器
		if (m_gameData.m_player->currentWeapon > 0) {
			auto& currWeapon = ResourceMgr::get().getActor(m_gameData.m_player->currentWeapon);
			if (currWeapon.baseHp < equipActor.baseHp || forceEquip) {
				m_gameData.m_player->attack -= currWeapon.baseAttack;
				m_gameData.m_player->attack += equipActor.baseAttack;
				m_gameData.m_player->currentWeapon = equipObjectId;
				result = true;
			}
		} else {
			//没有装备
			m_gameData.m_player->attack += equipActor.baseAttack;
			m_gameData.m_player->currentWeapon = equipObjectId;
			result = true;
		}
	} else if (equipActor.useCount == -3) { //防具
		if (m_gameData.m_player->currentArmor >= 0) {
			auto& currCloth = ResourceMgr::get().getActor(m_gameData.m_player->currentArmor);
			if (currCloth.baseHp < equipActor.baseHp || forceEquip) {
				m_gameData.m_player->defence -= currCloth.baseDef;
				m_gameData.m_player->defence += equipActor.baseDef;
				m_gameData.m_player->currentArmor = equipObjectId;
				result = true;
			}
		} else {
			m_gameData.m_player->defence += equipActor.baseDef;
			m_gameData.m_player->currentArmor = equipObjectId;
			result = true;
		}
	} else {
		assert(false);						//不可能
	}
	return result;
}

void Game::showGameInfoString(const std::string& errorString)
{
	SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", errorString.c_str());
	const SDL_MessageBoxButtonData buttons[] = {
		{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, u8"确定" },
	};
	const SDL_MessageBoxColorScheme colorScheme = {
		{ /* .colors (.r, .g, .b) */
			/* [SDL_MESSAGEBOX_COLOR_BACKGROUND] */
			{ 255,   0,   0 },
			/* [SDL_MESSAGEBOX_COLOR_TEXT] */
			{   0, 255,   0 },
			/* [SDL_MESSAGEBOX_COLOR_BUTTON_BORDER] */
			{ 255, 255,   0 },
			/* [SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND] */
			{   0,   0, 255 },
			/* [SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED] */
			{ 255,   0, 255 }
		}
	};
	const SDL_MessageBoxData messageboxdata = {
		SDL_MESSAGEBOX_INFORMATION, /* .flags */
		NULL, /* .window */
		u8"提示", /* .title */
		/*"select a button"*/errorString.c_str(), /* .message */
		SDL_arraysize(buttons), /* .numbuttons */
		buttons, /* .buttons */
		&colorScheme /* .colorScheme */
	};
	int buttonid;
	if (SDL_ShowMessageBox(&messageboxdata, &buttonid) < 0) {
		SDL_Log("error displaying message box");
		return;
	}
}

bool Game::tryDestroyAroundObjects(int objectId)
{
	int x = m_gameData.m_player->x;
	int y = m_gameData.m_player->y;
	auto pMap = m_gameData.maps[m_gameData.level];
	for (const auto& obj : pMap->m_objects) {
		if (!obj.alive) continue;
		if (obj.x == x && abs(obj.y - y) == 1) {

			return true;
		} else if (obj.y == y && abs(obj.x - x) == 1) {
			return true;
		}
	}
	return false;
}

bool Game::useBagItem(int itemType)
{
	bool bRet = false;
	bRet = 0;
	switch (itemType) {
	case 13: {          //火眼金睛
		//this.gameStateFlag = (byte)5;   //火眼金睛状态
		//this.void_a();
		break;
	}
	case 14: {          //生死簿
		//this.gameStateFlag = (byte)12;  //生死簿状态
		//this.void_a();
		break;
	}
	case 15: {          //筋斗云
		break;
	}
	case 16: {          //芭蕉扇
		if (!tryDestroyAroundObjects(10)) {  //检查火
			//this.a((byte)0, "你必须面对三昧真火再使用它。", (byte)0, (byte)0);
			showGameInfoString(u8"你必须面对三昧真火再使用它。");
			break;
		}
		//this.gameStateFlag = (byte)3;
		bRet = true;
		break;
	}
	case 17: {          //金勺子
		if (!tryDestroyAroundObjects(11)) { //检查能挖的墙
			showGameInfoString(u8"你必须面对一堵墙使用");
			//this.a((byte)0, "你必须面对一堵墙使用", (byte)0, (byte)0);
			break;
		}
		//this.gameStateFlag = (byte)3;
		bRet = true;
		break;
	}
	case 18: {          //玄明石 打开所有墙
		//itemType = (byte)this.spriteCount;
		auto pMap = m_gameData.maps[m_gameData.level];
		for (auto& obj : pMap->m_objects) {
			if (obj.objectId != 11) continue;
			obj.alive = false;
		}
		//while ((itemType = (byte)(itemType - 1)) >= 0) {
		//	if (this.spriteTypeArray[itemType] != 11) continue; //能挖的墙
		//	//this.void_e(itemType);  //清除物体
		//}
		//this.gameStateFlag = (byte)3;
		bRet = true;
		break;
	}
		   //恢复物品
	case 19: {  //千年月桂露
		int hp = (m_gameData.m_player->attack + m_gameData.m_player->defence) * 74 / 10;
		m_gameData.m_player->hp += hp;
		//this.updateHp(hp);
		//this.a((byte)0, "增加了" + hp + "血量", (byte)0, (byte)0);
		showGameInfoString(u8"增加了" + std::to_string(hp) + u8"血量");
		bRet = true;
		break;
	}
	case 20: {  //相形变位
		if (m_gameData.level == 40) {
			//this.a((byte)0, "本层不能直接瞬移。", (byte)0, (byte)0);
			showGameInfoString(u8"本层不能直接瞬移。");
			break;
		}
		/*int posX = this.m_width - 1 - this.playerXOffset;
		int posY = this.m_height - 1 - this.playerYOffest;
		boolean bl = this.boolean_a(posX, posY);
		if (bl) {
			this.void_a(posX, posY);
			this.i((this.preferWidth - 32 >> 1) - this.playerPixelX, (this.perferHeight - 32 >> 1) - this.playerPixelY);
			this.k();
		}
		if (bl) {
			this.gameStateFlag = (byte)3;
			n = true;
			break;
		}
		if (this.var_boolean_f) break;*/
		showGameInfoString(u8"无法移动到该位置");
		//this.a((byte)0, "无法移动到该位置", (byte)0, (byte)0);
		break;
	}
	case 21: {  //上行符
		//n = this.a(this.floor + 1, false, false);
		if (m_gameData.level + 1 > 55) {
			showGameInfoString(gameErrorString[1]);
			break;
		}
		changeLevel(m_gameData.level + 1);
		//this.pressedKeyCode = 0;
		//this.gameStateFlag = (byte)3;
		break;
	}
	case 22: {  //下行符
		if (m_gameData.level - 1 < 0) {
			showGameInfoString(gameErrorString[0]);
			break;
		}
		changeLevel(m_gameData.level - 1);
		/*n = this.a(this.floor - 1, true, false);
		this.pressedKeyCode = 0;
		this.gameStateFlag = (byte)3;*/
		break;
	}
	case 85: {      //黄金钥匙 开全部的黄门
		/*itemType = (byte)this.spriteCount;
		while ((itemType = (byte)(itemType - 1)) >= 0) {
			if (this.spriteTypeArray[itemType] != 1) continue;
			this.void_e(itemType);
		}
		this.gameStateFlag = (byte)3;*/
		auto pMap = m_gameData.maps[m_gameData.level];
		for (auto& obj : pMap->m_objects) {
			if (obj.objectId != 1)
				continue;
			obj.setDisappear();
		}
		bRet = true;
		break;
	}
	case 86: {      //日月无极钹 直接消灭前后左右一格的敌人
		int x = m_gameData.m_player->x;
		int y = m_gameData.m_player->y;
		killMonster(x, y - 1);
		killMonster(x, y + 1);
		killMonster(x - 1, y);
		killMonster(x + 1, y);
		//this.gameStateFlag = (byte)3;
		bRet = true;
	}
	}
	return bRet;
}

bool Game::killMonster(int x, int y)
{
	bool bRet = false;
	auto pMap = m_gameData.maps[m_gameData.level];
	for (auto& obj : pMap->m_objects) {
		if (obj.x != x || obj.y != y || !obj.alive)
			continue;
		if (obj.objectId == 5) {	//守卫
			obj.alive = false;
			std::vector<int> monsterDoorIndexs;
			if (!markMonsterDoorWillOpen(obj.npcFlag, monsterDoorIndexs))
				continue;
			//干掉所有的封印门
			for (int index : monsterDoorIndexs) {
				pMap->m_objects[index].state = GroundObjectState_Disappearing;
			}
		}

		auto& actorInfo = ResourceMgr::get().getActor(obj.objectId);
		//高级敌人或不是怪物或正在消失，忽略
		if (obj.objectId >= 67 || actorInfo.actorType != ActorType_Monster || obj.state == GroundObjectState_Disappearing)
			continue;
		//找到了合适的monster，干掉它
		obj.setDisappear();
		int money = actorInfo.moneyEarn;
		if (getBagItemIndex(25) >= 0) {		//有幸运金币
			money *= 2;
		}
		m_gameData.m_player->money += money;
		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "money increase: %d, now: %d", money, m_gameData.m_player->money);
		bRet = true;
	}
	return bRet;
}

bool Game::killMonster(GroundObject& obj)
{
	if (!obj.alive)
		return true;
	if (obj.objectId == 5) {	//守卫
		obj.alive = false;
		std::vector<int> monsterDoorIndexs;
		if (markMonsterDoorWillOpen(obj.npcFlag, monsterDoorIndexs)) {
			//干掉所有的封印门
			for (int index : monsterDoorIndexs) {
				obj.state = GroundObjectState_Disappearing;
			}
		}
	}
	auto& actorInfo = ResourceMgr::get().getActor(obj.objectId);
	//找到了合适的monster，干掉它
	obj.setDisappear();
	int money = actorInfo.moneyEarn;
	if (getBagItemIndex(25) >= 0) {		//有幸运金币
		money *= 2;
	}
	m_gameData.m_player->money += money;
	SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "money increase: %d, now: %d", money, m_gameData.m_player->money);
	return true;
}

bool Game::trade(int objectId, bool bConfirm)
{
	bool bResult = false;
	bool isTrade = true;
	auto pMap = m_gameData.maps[m_gameData.level];
	auto pos = std::find_if(pMap->m_objects.begin(), pMap->m_objects.end(), [objectId](const GroundObject& obj) {
		return obj.objectId == objectId;
	});
	if (pos == pMap->m_objects.end())
		return false;

	//执行交易操作
	auto& obj = *pos;
	bool isBusiness = obj.objectId != 77;			//太白金星则为false
	char highBit = (unsigned short)obj.npcFlag >> 8;
	char lowBit = (char)obj.npcFlag;
	if (lowBit <= 0)
		return false;
	if (bConfirm) {
		if ((highBit & 2) != 0) {
			if (isBusiness) {			//太白金星
				switch (lowBit) {
				case 2: {
					equipItem(13);		//火眼金睛
					break;
				}break;
				case 1:
				case 15:
				case 21: {
					m_gameData.m_player->money += 1000;
				}break;
				case 6: {
					equipItem(19);     //千年月桂露
				}break;
				}
			} else {	//天宫商人
				switch (lowBit) {
				case 1: {	//攻防加3%
					m_gameData.m_player->attack += m_gameData.m_player->attack * 3 / 100;
					m_gameData.m_player->defence += m_gameData.m_player->defence * 3 / 100;
				}break;
				case 3: {
					if (useMoney(50)) {
						changeKeyCount(KeyType::Blue, 1);
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 4: {
					if (useMoney(50)) {
						changeKeyCount(KeyType::Yellow, 5);
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 5: {
					//1000块一把黄钥匙
					if (useMoney(1000)) {
						changeKeyCount(KeyType::Yellow, 1);
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 6: {
					if (useMoney(800)) {
						++m_gameData.m_player->redKey;
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 7: {
					if (useMoney(200)) {
						changeKeyCount(KeyType::Blue, 1);
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 8: {   //卖黄钥匙
					if (changeKeyCount(KeyType::Yellow, -1)) {
						m_gameData.m_player->money += 100;
					} else {
						showGameInfoString(u8"没有黄钥匙");
						isTrade = false;
					}
				}break;
				case 9: {
					if (useMoney(1000)) {
						changeKeyCount(KeyType::Yellow, 1);
						changeKeyCount(KeyType::Blue, 1);
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 10: {
					if (useMoney(200)) {
						changeKeyCount(KeyType::Yellow, 3);
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 11: {
					if (useMoney(2000)) {
						changeKeyCount(KeyType::Blue, 2);
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 12: {
					if (useMoney(1000)) {
						m_gameData.m_player->hp += 2000;
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				case 13: {
					if (useMoney(4000)) {
						equipItem(18); //玄明石
					} else {
						showGameInfoString(u8"没有足够的金钱");
						isTrade = false;
					}
				}break;
				default:
					break;
				}
			}
			if (isTrade) {
				if ((highBit & 4) == 0) {
					obj.npcFlag ^= 0x200;
					if ((highBit & 1) != 0) {
						//this.showGameMessageBox(isBusninessMan, s - 1);
						if (isBusiness == false) {	//太白金星
							showGameInfoString(var_java_lang_String_arr_g[lowBit - 1]);
						} else {					//天宫商人
							showGameInfoString(var_java_lang_String_arr_j[lowBit - 1]);
						}
						bResult = true;
					} else {
						//直接让商人消失
						obj.setDisappear();
					}
				}
			} else {
				bResult = true;
			}
		} else if ((highBit & 1) != 0) {
			obj.npcFlag ^= 0x100;
			if ((highBit & 4) == 0) {
				obj.setDisappear();
			}
		}
	} else if ((highBit & 2) == 0 && (highBit & 1) != 0) {
		obj.npcFlag = obj.npcFlag ^ 0x100;
		if ((highBit & 4) == 0) {
			obj.setDisappear();
		}
	}
	return bResult;
}

bool Game::useMoney(int money)
{
	if (m_gameData.m_player->money >= money) {
		m_gameData.m_player->money -= money;
		return true;
	}
	return false;
}

bool Game::changeKeyCount(KeyType type, int count)
{
	switch (type) {
	case KeyType::Yellow: {
		if (m_gameData.m_player->yellowKey + count < 0)
			return false;
		m_gameData.m_player->yellowKey += count;
	}break;
	case KeyType::Blue: {
		if (m_gameData.m_player->blueKey + count < 0)
			return false;
		m_gameData.m_player->blueKey += count;
	}break;
	case KeyType::Red: {
		if (m_gameData.m_player->redKey + count < 0)
			return false;
		m_gameData.m_player->redKey += count;
	}break;
	default:
		return false;
		break;
	}
	return true;
}

bool Game::markMonsterDoorWillOpen(int npcFlag, std::vector<int> openDoorIndexs)
{
	auto pMap = m_gameData.maps[m_gameData.level];
	for (int i = 0; i < pMap->m_objects.size(); i++) {
		auto& obj = pMap->m_objects[i];
		if (obj.objectId == 5) {
			//存在封印门，返回失败
			openDoorIndexs.clear();
			return false;
		}
		if (obj.objectId != 4)	//如果不是封印门，忽略
			continue;
		openDoorIndexs.push_back(i);
	}
	return true;
}

void Game::paintSprite(std::shared_ptr<GLTexture> pTexture, int destX, int destY, int srcX, int srcY, int width, int height)
{
	auto pRenderer = SDL_GetRenderer(m_window);
	SDL_Rect srcRect{ srcX, srcY, width, height };
	SDL_Rect dstRect{ destX, destY, width,height };
	SDL_RenderCopy(pRenderer, pTexture->texture, &srcRect, &dstRect);
}

void Game::paintSprite(std::shared_ptr<GLTexture> pTexture, int destX, int destY)
{
	paintSprite(pTexture, destX, destY, 0, 0, pTexture->width, pTexture->height);
}

bool Game::activeObject(int nextX, int nextY)
{
	bool bPassable = true;

	auto& player = m_gameData.m_player;
	//判断是否有物体阻挡了自己
	std::vector<GroundObject*> objects = getGroundObjectsAtPosition(nextX, nextY);

	if (objects.empty()) {
		bPassable = checkMapPassable(nextX, nextY);
		return bPassable;
	}
	for (auto& pObj : objects) {
		if (!pObj->alive) continue;
		switch (pObj->actorType) {
		case ActorType_GroundItem: {
			switch (pObj->objectId) {
			case 7:			//上楼梯
			case 8: {		//下楼梯
				//更新楼层
			}break;
			case 83: {		//剧情点
				/*
				if (!this.b((int)this.npcFacilityFlagArray[n3], false)) break;
					this.var_boolean_f = false;
					this.var_java_lang_String_c = null;
					n5 = this.boolean_d(this.npcFacilityFlagArray[n3]) ? 1 : 0;
					n7 = 0;
					break block0;
				*/
			}break;
			case 1: {			//黄门
				if (m_gameData.m_player->yellowKey) {
					m_gameData.m_player->yellowKey--;
					pObj->setDisappear();
				} else {
					showGameInfoString(u8"你没有黄钥匙");
					bPassable = false;
				}
			}break;
			case 3: {			//蓝门
				if (m_gameData.m_player->blueKey) {
					m_gameData.m_player->blueKey--;
					pObj->setDisappear();
				} else {
					showGameInfoString(u8"你没有蓝钥匙");
					bPassable = false;
				}
			}break;
			case 2: {
				if (m_gameData.m_player->redKey) {
					m_gameData.m_player->redKey--;
					pObj->setDisappear();
				} else {
					showGameInfoString(u8"你没有红钥匙");
					bPassable = false;
				}
			}break;
			case 81: 			//封印门
			case 4: {			//封印门
				if (!pObj->bVisible) break;
				bPassable = false;
				showGameInfoString(u8"障碍物：封印门\n需要消灭指定的怪物才能打开的门！");
			}break;
			case 9: {			//炼丹炉
				if (m_gameData.m_player->direction != MoveDirection::Up) break;
				bPassable = false;
				//TODO:
			}break;
			case 10: {			//云雾（三昧真火）
				bPassable = false;
				showGameInfoString(u8"障碍物：三昧真火\n必须用芭蕉扇才能熄灭它.");
			}break;
			case 11: {
				if (m_gameData.level != 23) {
					showGameInfoString(u8"障碍物：墙\n只有金勺子、玄明石可以凿开。或者剧情打开！");
				}
				bPassable = false;
			}break;
			case 12: {				//隐形墙
				pObj->objectId = 11;						//更改为能挖的墙
				pObj->initPropertys();						//刷新属性
				pObj->state = GroundObjectState_Appearing;	//设置状态为出现
				pObj->animSequenceIndex = 9;				//更新动画序列帧索引
				pObj->bVisible = true;						//设置为显示
				bPassable = false;
			}break;
			case 6: {
				if (pObj->bVisible) break;					//如果已经显示了，忽略
				pObj->bVisible = true;						//设置为显示
				pObj->state = GroundObjectState_Appearing;	//设置状态为出现
				pObj->animSequenceIndex = 9;				//更新序列帧索引
			}break;
			default:
				break;
			}
		}break;
		case ActorType_PropertyItem: {
			pickupGroundItem(pObj->objectId);
			pObj->alive = false;
		}break;
		case ActorType_Equip: {
			pickupGroundItem(pObj->objectId);
			pObj->alive = false;
		}break;
		case ActorType_Monster: {
			bPassable = false;
			auto hp = caculateHpReduce(pObj->objectId, true);
			if (hp < 0 || hp > player->hp) {
				SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "You can't defeat it!");
				showGameInfoString(u8"你无法战胜它");
				return bPassable;
			} else {
				SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "You beat the monster, hp reduce: %d, rest: %d", hp, player->hp - hp);
			}
			killMonster(*pObj);

		}break;
		case ActorType_BusinessMan: {
			bPassable = false;
			bool bConfirm = true;
			//this.currentBussinessManId = floor;
			//n5 = this.npcFacilityFlagArray[floor];
			char highBit = (unsigned short)pObj->npcFlag >> 8;
			char lowBit = (char)pObj->npcFlag;
			//byte by2 = (byte)(n5 >> > 8);
			//n5 = (byte)n5;
			//int n8 = floor = this.objectIdLookupTable[floor] == 77 ? 0 : 1; //太白金星
			bool isBusiness = pObj->objectId != 77;
			if (highBit > 0) {
				lowBit = lowBit - 1;
				if ((highBit & 2) != 0) {
					//switch (floor) {
					if (isBusiness) {   //太白金星
						bConfirm = false;
						//this.a((byte)4, this.businessManTaiBaiTalks[lowBit], (byte)0, (byte)0);
						showGameInfoString(helpNpcTalks[lowBit]);
					} else {
						//this.a((byte)4, this.var_java_lang_String_arr_i[lowBit], (byte)1, (byte)2);
						showGameInfoString(businessManTalks[lowBit]);
					}
				} else {
					//this.j(isBusiness, lowBit);
					if (isBusiness) {
						//this.a((byte)4, this.var_java_lang_String_arr_g[lowBit], (byte)0, (byte)0);
						showGameInfoString(var_java_lang_String_arr_g[lowBit]);
					} else {
						//this.a((byte)4, this.var_java_lang_String_arr_j[lowBit], (byte)1, (byte)2);
						showGameInfoString(var_java_lang_String_arr_g[lowBit]);
					}
				}
			}
			// 
			trade(pObj->objectId, true);
		}break;
		case ActorType_NPC: {
			bPassable = false;
		}break;
		default:
			break;
		}
	}
	return bPassable;
}

void GroundObject::initPropertys()
{
	auto& actor = ResourceMgr::get().getActor(objectId);
	this->frameWidth = actor.spriteFrameWidth;
	this->frameHeight = actor.spriteFrameHeight;
	this->animSequenceIndex = actor.animSequenceIndex;
	this->currFrameIndex = 0;
	this->actorType = actor.actorType;
}

void GroundObject::setDisappear()
{
	this->state = GroundObjectState_Disappearing;
	switch (this->actorType) {
	case ActorType_GroundItem: {
		switch (objectId) {
		case 1:				//黄门
		case 2:				//红门
		case 3: {			//蓝门
			frameWidth = 45;
			frameHeight = 56;
			animSequenceIndex = 6;
			currFrameIndex = 0;
		}break;
		case 11: {			//能挖的墙
			frameWidth = 45;
			frameHeight = 57;
			animSequenceIndex = 2;
			currFrameIndex = 0;
		}break;
		default:
			break;
		}
	}break;
	case ActorType_PropertyItem:
	case ActorType_Equip: {
		frameWidth = 45;
		frameHeight = 56;
		animSequenceIndex = 10;
		currFrameIndex = 0;
	}break;
	case ActorType_Monster:
	case ActorType_BusinessMan:
	case ActorType_NPC: {
		frameWidth = 27;
		frameHeight = 29;
		animSequenceIndex = 6;
		currFrameIndex = 0;
	}break;
	default:
		break;
	}
}

void GroundObject::setAppear()
{
	state = GroundObjectState_Appearing;
	switch (actorType) {
	case ActorType_GroundItem:
		break;
	case ActorType_PropertyItem:
	case ActorType_Monster:
	case ActorType_BusinessMan:
	case ActorType_NPC: {
		frameWidth = 27;
		frameHeight = 29;
		animSequenceIndex = 8;
		currFrameIndex = 0;
	}break;
	}
}

void GroundObject::nextAnim()
{
	if (!alive || !bVisible || animSequenceIndex <= 0) return;
	if (currFrameIndex < objectAnimSequenceArray[animSequenceIndex].size() - 1) {
		currFrameIndex++;
		return;
	}
	switch (state) {
	case GroundObjectState_Disappearing: {
		alive = false;		//动画播放完后设置死亡标志位
		return;
	}break;
	case GroundObjectState_Appearing: {
		initPropertys();	//重新设置属于actor的动画序列帧
		state = GroundObjectState_Normal;
		return;
	}break;
	}
	currFrameIndex = 0;
}

int GroundObject::getAnimIndex()
{
	if (animSequenceIndex < 0 || animSequenceIndex > objectAnimSequenceArray.size())
		return 0;
	if (currFrameIndex <0 || currFrameIndex > objectAnimSequenceArray[animSequenceIndex].size())
		return 0;
	return objectAnimSequenceArray[animSequenceIndex][currFrameIndex];
}